import encodePosition from './encode';
import decodePosition from './decode';
import Selection from './selection';
import { toArray } from './utils';

// const USE_EXTEND = typeof window.getSelection().extend === 'function'

/**
 * Choice is a module for unobtrusively saving and restoring selections
 * in a contenteditable element.
 */
class Choice {
  document: Document;
  elem: HTMLElement;

  /*
   * @param {Element} rootElem
   * @param {Function} getChildren
   */
  constructor(document: Document) {
    this.document = document;
    const rootElem = document.body;
    // Because of some Firefox bugs.
    if (!rootElem.contentEditable) {
      throw TypeError('Choice requires a contentEditable element.');
    }
    this.elem = rootElem;
  }

  _getChildren() {
    return toArray(this.elem.childNodes);
  }

  /**
   * getSelection() returns an instance of Choice.Selection with
   * information about the start and end points of the selection.
   *
   */
  getSelection(): Selection | null {
    let sel = this.document.getSelection()!;
    if (!sel.rangeCount || this.document.activeElement !== this.elem) {
      return null;
    }

    let children = this._getChildren();
    let start = encodePosition(children, sel.anchorNode!, sel.anchorOffset);
    let end = null;

    if (sel.isCollapsed) {
      end = start;
    } else if (start) {
      end = encodePosition(children, sel.focusNode!, sel.focusOffset);
    }
    return start && end ? new Selection(start, end) : null;
  }

  /**
   * restore(selection) restores a selection from an instance of
   * Selection.
   */
  restore(selection: Selection) {
    let sel = this.document.getSelection()!;

    if (!(selection instanceof Selection)) {
      throw TypeError(`${selection} is not a valid selection.`);
    }

    let children = this._getChildren();
    let start, end;

    if (selection.isCollapsed) {
      start = decodePosition(children[selection.end[0]], selection.end[1]);
    } else {
      start = decodePosition(children[selection.start[0]], selection.start[1]);
      end = decodePosition(children[selection.end[0]], selection.end[1]);
    }

    this.elem.focus();

    let range = this.document.createRange();
    if (typeof this.document.getSelection()!.extend === 'function') {
      range.setStart(start.node, start.offset);
      range.setEnd(start.node, start.offset);

      sel.removeAllRanges();
      sel.addRange(range);

      if (end) {
        sel.extend(end.node, end.offset);
      }
    } else {
      end = end || start;
      if (selection.isBackwards) {
        [start, end] = [end, start];
      }

      range.setStart(start.node, start.offset);
      range.setEnd(end.node, end.offset);
      sel.removeAllRanges();
      sel.addRange(range);
    }
  }
}

export default Choice;
